﻿using System;
using System.IO;
using System.Xml;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.Data.SqlClient;

using Fuse8.DomainFramework.Common;
using Fuse8.DomainFramework.DataAccess.SqlDatabase;

using Fuse8.ContentManagementFramework.Domain;
using Fuse8.ContentManagementFramework.Domain.Commands.CommandParameters;

namespace Fuse8.ContentManagementFramework.DataAccess.SqlAccess.Mappers
{
    public class CmsEntityAnnotationDataMapper : BaseSqlEntityDataMapper<CmsEntityAnnotation, int>
    {
        private static BaseSqlEntityDataMapper<CmsMediaContent, int> _based = new BaseSqlEntityDataMapper<CmsMediaContent, int>(new string[] { "Comments" });

        private static Dictionary<string, Func<object, IList<SqlParameter>>> _mappersTo = new Dictionary<string, Func<object, IList<SqlParameter>>>();

        static CmsEntityAnnotationDataMapper()
        {
            _mappersTo.Add("cms_sp_EntityAnnotationSaveMany",
                (p) =>
                {
                    return MapForAddMany(p);
                });
        }

        public CmsEntityAnnotationDataMapper()
            : base()
        { }

        public override IList<SqlParameter> MapTo(string sp, object parameter, bool isForCreate)
        {
            Guard.ArgumentNotEmpty(sp, "sp");
            Guard.ArgumentNotNull(parameter, "parameter");

            return _mappersTo.ContainsKey(sp) ?
                _mappersTo[sp](parameter) :
                base.MapTo(sp, parameter, isForCreate);
        }

        public static IList<SqlParameter> MapForAddMany(object parameter)
        {
            Guard.ArgumentNotNull(parameter, "parameter");

            IList<SqlParameter> result = new List<SqlParameter>();

            var concreteParameter = parameter as CmsEntityAnnotationSaveForEntityCommandParameter;
            if (concreteParameter != null)
            {
                result.Add(new SqlParameter("EntityId", concreteParameter.EntityId));
                result.Add(new SqlParameter("EntityTypeParameter", concreteParameter.EntityTypeParameter));
                if (concreteParameter.Annotations != null)
                {
                    result.Add(new SqlParameter("ContentXml", CreateContentXml(concreteParameter.Annotations)));
                }
                if ((concreteParameter.AnnotationsForDelete != null) && concreteParameter.AnnotationsForDelete.Any())
                {
                    result.Add(
                        new SqlParameter(
                            "AnnotationsForDelete",
                            concreteParameter.AnnotationsForDelete
                                .Select(p => p.ToString())
                                .Aggregate(
                                    (working, next) =>
                                    {
                                        return string.Format("{0}, {1}", working, next);
                                    })));
                }
                if ((concreteParameter.CommentsForDelete != null) && concreteParameter.CommentsForDelete.Any())
                {
                    result.Add(
                        new SqlParameter(
                            "CommentsForDelete",
                            concreteParameter.CommentsForDelete
                                .Select(p => p.ToString())
                                .Aggregate(
                                    (working, next) =>
                                    {
                                        return string.Format("{0}, {1}", working, next);
                                    })));
                }
                result.Add(new SqlParameter("CreatedBy", concreteParameter.CreatedBy));
                result.Add(new SqlParameter("DateCreated", concreteParameter.DateCreated));
                result.Add(new SqlParameter("LastUpdatedBy", concreteParameter.LastUpdatedBy));
                result.Add(new SqlParameter("DateLastUpdated", concreteParameter.DateLastUpdated));
            }

            return result;
        }

        private static string CreateContentXml(IEnumerable<CmsEntityAnnotation> annotations)
        {
            string result = string.Empty;

            using (MemoryStream stream = new MemoryStream())
            {
                using (XmlTextWriter writer = new XmlTextWriter(stream, Encoding.Unicode))
                {
                    using (StreamReader reader = new StreamReader(writer.BaseStream, Encoding.Unicode))
                    {
                        //<xml>
                        writer.WriteStartDocument();
                        //<Annotations>
                        writer.WriteStartElement("Annotations");

                        foreach (var item in annotations)
                        {
                            //<Annotation>
                            writer.WriteStartElement("Annotation");

                            //<Id>
                            writer.WriteStartElement("Id");
                            writer.WriteValue(item.Id);
                            //</Id>
                            writer.WriteEndElement();

                            //<AnnotationText>
                            writer.WriteStartElement("AnnotationText");
                            writer.WriteValue(item.AnnotationText);
                            //</AnnotationText>
                            writer.WriteEndElement();

                            //<X>
                            writer.WriteStartElement("X");
                            writer.WriteValue(item.X);
                            //</X>
                            writer.WriteEndElement();

                            //<Y>
                            writer.WriteStartElement("Y");
                            writer.WriteValue(item.Y);
                            //</Y>
                            writer.WriteEndElement();

                            //<Z>
                            writer.WriteStartElement("Z");
                            writer.WriteValue(item.Z);
                            //</Z>
                            writer.WriteEndElement();

                            //<IsMinimized>
                            writer.WriteStartElement("IsMinimized");
                            writer.WriteValue(item.IsMinimized);
                            //</IsMinimized>
                            writer.WriteEndElement();

                            //<Comments>
                            writer.WriteStartElement("Comments");
                            if (item.Comments != null)
                            {
                                foreach (var comment in item.Comments)
                                {
                                    //<Comment>
                                    writer.WriteStartElement("Comment");

                                    //<Id>
                                    writer.WriteStartElement("Id");
                                    writer.WriteValue(comment.Id);
                                    //</Id>
                                    writer.WriteEndElement();

                                    //<AnnotationId>
                                    writer.WriteStartElement("AnnotationId");
                                    writer.WriteValue(item.Id);
                                    //</AnnotationId>
                                    writer.WriteEndElement();

                                    //<CommentText>
                                    writer.WriteStartElement("CommentText");
                                    writer.WriteValue(comment.CommentText);
                                    //</CommentText>
                                    writer.WriteEndElement();

                                    //<OrderIndex>
                                    writer.WriteStartElement("OrderIndex");
                                    writer.WriteValue(comment.OrderIndex);
                                    //</OrderIndex>
                                    writer.WriteEndElement();

                                    //</Comment>
                                    writer.WriteEndElement();
                                }
                            }
                            //</Comments>
                            writer.WriteEndElement();

                            //</Annotation>
                            writer.WriteEndElement();
                        }

                        //</Annotations>
                        writer.WriteEndElement();
                        //</xml>
                        writer.WriteEndDocument();
                        
                        writer.Flush();

                        stream.Position = 0;

                        result = reader.ReadToEnd();
                    }
                }
            }

            return result;
        }
    }
}
